/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package trabalhocompiladores.entidade;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import trabalhocompiladores.entidade.automato.Operacoes.*;

/**
 *
 * @author u031254
 */
public class Automato {

    private HashMap<Integer, Estado> Estados;
//    private HashMap<Integer, Estado> finalState;
//    private HashSet<Transacao> transitions;
//    private Estado estadoInicial;

    public Automato() {
        this.Estados = new HashMap<Integer, Estado>();
        //       this.finalState = new HashMap<Integer, Estado>();
//        this.transitions = new HashSet<Transacao>;
//        this.estadoInicial = new Estado();
        this.Estados.put(0, new Estado(0));
    }

    public HashMap<Integer, Estado> getEstados() {
        return Estados;
    }

    public void setEstados(HashMap<Integer, Estado> Estados) {
        this.Estados = Estados;
    }

    public void gerarAFN(String expressaoRegular) {

        //Gerando a lista de expressoes
        List<Expressao> listaExpressao = new ArrayList<>();
        Expressao expressao;
        String possivelOperador;
        int teste = expressaoRegular.length();
        for (int i = 0; i < teste; i++) {
            possivelOperador = "";
            String c = "" + expressaoRegular.charAt(i);


            //verifica se tem parentese, ele gera toda a expressao e se tiver digito tambem
            if (expressaoRegular.charAt(i) == '(') {
                i++;
                String stringAux = "";
                for (int x = (i); (x < expressaoRegular.length() && expressaoRegular.charAt(x) != ')'); x++) {
                    stringAux += expressaoRegular.charAt(x);
                }
                i += ((stringAux.length()));

                /* Try Feito para detectar se o proximo caractere é um operador, se for ele faz a operação
                 caso não seja ele não fara pois haverá um exeção e o sistema vai entender que ja ultrapassou
                 a expressao */

                try {
                    if (expressaoRegular.charAt(i + 1) == '*' || expressaoRegular.charAt(i + 1) == '+') {
                        possivelOperador += expressaoRegular.charAt(i + 1);
                        i++;
                    }
                } catch (StringIndexOutOfBoundsException e) {
                }
//                    if(expressaoRegular.charAt(i) == '*' || expressaoRegular.charAt(i) == '+' ){
//                        possivelOperador += expressaoRegular.charAt(i);
//                        i++;
//                    }
                expressao = new Expressao(stringAux, possivelOperador);

            } else if (expressaoRegular.charAt(i) == '|') {
                expressao = new Expressao(null, "" + expressaoRegular.charAt(i));

            } else {
                /* Try Feito para detectar se o proximo caractere é um operador, se for ele faz a operação
                 caso não seja ele não fara pois haverá um exeção e o sistema vai entender que ja ultrapassou
                 a expressao */
                try {
                    if (expressaoRegular.charAt(i + 1) == '*' || expressaoRegular.charAt(i + 1) == '+') {
                        possivelOperador += expressaoRegular.charAt(i + 1);
                        i++;
                    }
                } catch (StringIndexOutOfBoundsException e) {
                }

                expressao = new Expressao(c, possivelOperador);
            }
            listaExpressao.add(expressao);

        }

        /*algoritimo usado para resolver a união de expressões, caso tenha uma 
         * união ele vai juntar a expressão anterior e a proxima expressão e concatenar com o operando |
         */
        Expressao expressaoAnterior, expressaoProxima;

        for (int i = 0; i < listaExpressao.size(); i++) {
            Expressao expressao1 = listaExpressao.get(i);

            if ("|".equals(expressao1.getTipoOperacao())) {
                expressaoAnterior = listaExpressao.get(i - 1);
                expressaoProxima = listaExpressao.get(i + 1);
                expressao1.setPrimeiroFilho(expressaoAnterior);
                expressao1.setSegundoFilho(expressaoProxima);
                listaExpressao.remove(expressaoAnterior);
                listaExpressao.remove(expressaoProxima);
            }

        }
        resolverExpressao(listaExpressao);
    }
    /*
     metodo criado para resolver as expressoes criadas
     */

    public void resolverExpressao(List<Expressao> listaExpressao) {

        IOperacoes operacao;

        for (int i = 0; i < listaExpressao.size(); i++) {
            Expressao expressao = listaExpressao.get(i);


            switch (expressao.getTipoOperacao()) {
                case "*":
                    operacao = new OperacaoMultiplicacao();
                    break;
                case "|":
                    operacao = new OperacaoUniao();
                    break;
                case "+":
                    operacao = new OperacaoSoma();
                    break;
                default:
                    operacao = new OperacaoPadrao();
            }
            operacao.gerarEstados(this, expressao);
        }


    }

    public void resolverExpressao(Expressao expressao) {

        IOperacoes operacao;
        switch (expressao.getTipoOperacao()) {
            case "*":
                operacao = new OperacaoMultiplicacao();
                break;
            case "|":
                operacao = new OperacaoUniao();
                break;
            case "+":
                operacao = new OperacaoSoma();
                break;
            default:
                operacao = new OperacaoPadrao();
        }
        operacao.gerarEstados(this, expressao);
    }

    public DefaultTableModel desenharMatriz() {
        
        DefaultTableModel tabelaModelo;
        Object[][] rowData;
        List<Object>[] lista = new ArrayList[3];
        
        for (int i = 0; i < lista.length; i++) {
            lista[i] =  new ArrayList<>();
        }
        
        
        
        
        for (Map.Entry<Integer, Estado> mapa : Estados.entrySet()) {
            Estado estado = mapa.getValue();
            for (Iterator it = estado.getLista().iterator(); it.hasNext();) {
                Transacao transacao = (Transacao) it.next();

                switch (transacao.getValor()) {
                    case "e":
                        lista[0].add(transacao.getEstadoIn().getInfo()+"-"+transacao.getEstadoFim().getInfo());
                        break;
                    case "a":
                        lista[1].add(transacao.getEstadoIn().getInfo()+"-"+transacao.getEstadoFim().getInfo());
                        break;
                    case "b":
                        lista[2].add(transacao.getEstadoIn().getInfo()+"-"+transacao.getEstadoFim().getInfo());
                        break;
                }
            }
        }
        List<Integer> teste = new LinkedList<>();
        teste.add(lista[0].size());
        teste.add(lista[1].size());
        teste.add(lista[2].size());
       Integer maxLinha = Collections.max(teste);

        Object[] colunas = {"e","a","b"};
        
        
        rowData = new Object[maxLinha][colunas.length];
        for (int i = 0; i < lista.length; i++) {
            for (int j = 0; j < lista[i].size(); j++) {
                
              rowData[j][i] = lista[i].get(j);
            }
            
        }
        
       tabelaModelo =  new DefaultTableModel(rowData, colunas);
       return tabelaModelo;
        
    }
}
